package com.manager.commons.file;

//                   _ooOoo_    
//                  o8888888o    
//                  88" . "88    
//                  (| -_- |)    
//                   O\ = /O    
//               ____/'---'\____    
//              /. '\\| : |//' .\    
//             // \\||| : |||// \\   
//            // _|||||-:-|||||_ \\    
//           // | | \\\ - /// | | \\
//           || |_| ''\---/'' |_| || 
//            \ .-\___/'-'\___/-. /    
//          ___\. .' /--.--\ '. ./___    
//        .""'< `.___\_<|>_/___.' >'"".    
//       | |: `- \`.|'\ _ /'|.`/ -` :| |    
//        \ \ `-. \_ __\ /__ _/ .-` / /    
//======'-.___`-.___\_______/___.-`___.-'======    
//                   '=---='    
//.................................................    
//写一哈代码，你就青叫唤；信龟儿设计，你逗是傻逼。
//永无Bug，啷个可能！求佛主保佑也没球用！

import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.channels.FileChannel;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;

import com.manager.commons.log.LogFactory;
import com.manager.commons.utils.StringUtil;

/**
 * FileUtil
 * @author H
 * @version V1.0
 */
public class FileUtil {

	protected static Logger logger = LogFactory.getLogger(FileUtil.class);

	/**
	 * 文件后缀
	 * @param filepath
	 * @return
	 */
	public static String getFileSuffix(String filepath) {
		return filepath.substring(filepath.lastIndexOf(".") + 1).toLowerCase();
	}

	/**
	 * 获取后缀
	 * @param str
	 * @param num
	 * @return
	 */
	public static String getSubStr(String str, int num) {
		String result = "";
		int i = 0;
		while (i < num) {
			int lastFirst = str.lastIndexOf('/');
			result = str.substring(lastFirst) + result;
			str = str.substring(0, lastFirst);
			i++;
		}
		return result.substring(1);
	}

	/**
	 * 获取地址
	 * @param path
	 * @param request
	 * @return
	 */
	public static String getPath(String path, HttpServletRequest request) {
		String root = "";
		String sp = getSubStr(path, 2);
		String savePath = request.getSession().getServletContext().getRealPath(root + sp);
		String projectName = request.getContextPath().replaceAll("/", "");
		String OSName = System.getProperty("os.name").toLowerCase();
		if (OSName.indexOf("windows") == -1) {
			String tempName = getSubStr(savePath, 2);
			projectName = tempName.substring(0, tempName.length() - 1);
		}
		savePath = savePath.replace(projectName, "res");
		return savePath;
	}

	/**
	 * 文件大小
	 * @param path
	 * @param type
	 * @param request
	 * @return
	 */
	public static Long getFileSize(String path, HttpServletRequest request) {
		String filepath = getPath(path, request);
		File file = new File(filepath);
		// 文件
		if (file.exists() && file.isFile()) {
			return file.length();
		} else {
			return null;
		}
	}

	/**
	 * 文件复制
	 * @param sourceFile
	 * @param targetFile
	 * @throws IOException
	 */
	public static void fileChannelCopy(File sourceFile, File targetFile) throws IOException {
		FileInputStream fi = null;
		FileOutputStream fo = null;
		FileChannel in = null;
		FileChannel out = null;
		try {
			fi = new FileInputStream(sourceFile);
			fo = new FileOutputStream(targetFile);
			in = fi.getChannel();// 得到对应的文件通道
			out = fo.getChannel();// 得到对应的文件通道
			in.transferTo(0, in.size(), out);// 连接两个通道，并且从in通道读取，然后写入out通道
		} finally {
			try {
				fi.close();
				in.close();
				fo.close();
				out.close();
			} catch (IOException e) {
				logger.error(e.getMessage(), e);
			}
		}
	}

	private static final int BUFFER_SIZE = 16 * 1024;

	/**
	 * 复制
	 * @param src
	 * @param dst
	 */
	public void copy(File src, File dst) {
		try {
			InputStream in = null;
			OutputStream out = null;
			try {
				in = new BufferedInputStream(new FileInputStream(src), BUFFER_SIZE);
				out = new BufferedOutputStream(new FileOutputStream(dst), BUFFER_SIZE);
				byte[] buffer = new byte[BUFFER_SIZE];
				while (in.read(buffer) > 0) {
					out.write(buffer);
				}
			} finally {
				if (null != in) {
					in.close();
				}
				if (null != out) {
					out.close();
				}
			}
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
	}

	/**
	 * 复制文件
	 * @param sourceFile
	 * @param targetFile
	 * @throws IOException
	 */
	public static void copyFile(File sourceFile, File targetFile) throws IOException {
		BufferedInputStream inBuff = null;
		BufferedOutputStream outBuff = null;
		try {
			// 新建文件输入流并对它进行缓冲
			inBuff = new BufferedInputStream(new FileInputStream(sourceFile));

			// 新建文件输出流并对它进行缓冲
			outBuff = new BufferedOutputStream(new FileOutputStream(targetFile));

			// 缓冲数组
			byte[] b = new byte[1024 * 5];
			int len;
			while ((len = inBuff.read(b)) != -1) {
				outBuff.write(b, 0, len);
			}
			// 刷新此缓冲的输出流
			outBuff.flush();
		} finally {
			// 关闭流
			if (inBuff != null)
				inBuff.close();
			if (outBuff != null)
				outBuff.close();
		}
	}

	/**
	 * 复制目录
	 * @param sourceDirPath
	 * @param targetDirPath
	 * @throws IOException
	 */
	public static void copyDir(String sourceDirPath, String targetDirPath) throws IOException {
		// 创建目标文件夹
		(new File(targetDirPath)).mkdirs();
		// 获取源文件夹当前下的文件或目录
		File[] file = (new File(sourceDirPath)).listFiles();
		for (int i = 0; i < file.length; i++) {
			if (file[i].isFile()) {
				// 复制文件
				String type = file[i].getName().substring(file[i].getName().lastIndexOf(".") + 1);
				if (type.equalsIgnoreCase("txt")) {
					copyFile(file[i], new File(targetDirPath + file[i].getName()), "utf-8", "gbk");
				} else {
					copyFile(file[i], new File(targetDirPath + file[i].getName()));
				}
			}
			if (file[i].isDirectory()) {
				// 复制目录
				String sourceDir = sourceDirPath + File.separator + file[i].getName();
				String targetDir = targetDirPath + File.separator + file[i].getName();
				copyDirectiory(sourceDir, targetDir);
			}
		}
	}

	/**
	 * 复制文件夹
	 * @param sourceDir
	 * @param targetDir
	 * @throws IOException
	 */
	public static void copyDirectiory(String sourceDir, String targetDir) throws IOException {
		// 新建目标目录
		(new File(targetDir)).mkdirs();
		// 获取源文件夹当前下的文件或目录
		File[] file = (new File(sourceDir)).listFiles();
		for (int i = 0; i < file.length; i++) {
			if (file[i].isFile()) {
				// 源文件
				File sourceFile = file[i];
				// 目标文件
				File targetFile = new File(new File(targetDir).getAbsolutePath() + File.separator + file[i].getName());
				copyFile(sourceFile, targetFile);
			}
			if (file[i].isDirectory()) {
				// 准备复制的源文件夹
				String dir1 = sourceDir + "/" + file[i].getName();
				// 准备复制的目标文件夹
				String dir2 = targetDir + "/" + file[i].getName();
				copyDirectiory(dir1, dir2);
			}
		}
	}

	/**
	 * 把文件转换为GBK文件
	 * @param srcFileName
	 * @param destFileName
	 * @param srcCoding
	 * @param destCoding
	 * @throws IOException
	 */
	public static void copyFile(File srcFileName, File destFileName, String srcCoding, String destCoding) throws IOException {
		BufferedReader br = null;
		BufferedWriter bw = null;
		try {
			br = new BufferedReader(new InputStreamReader(new FileInputStream(srcFileName), srcCoding));
			bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(destFileName), destCoding));
			char[] cbuf = new char[1024 * 5];
			int len = cbuf.length;
			int off = 0;
			int ret = 0;
			while ((ret = br.read(cbuf, off, len)) > 0) {
				off += ret;
				len -= ret;
			}
			bw.write(cbuf, 0, off);
			bw.flush();
		} finally {
			if (br != null)
				br.close();
			if (bw != null)
				bw.close();
		}
	}

	/**
	 * 删除文件
	 * @param path
	 * @param type
	 * @param request
	 */
	public static void deleteFile(String path, HttpServletRequest request) {
		String savePath = getPath(path, request);
		// 删除磁盘无用的图片资源文件
		deleteFile(new File(savePath));
	}

	/**
	 * 删除图片文件
	 * @param file
	 */
	public static void deleteFile(File file) {
		// 判断文件是否存在
		if (file.exists()) {
			// 判断是否是文件
			if (file.isFile()) {
				// 删除文件
				file.delete();
			} else if (file.isDirectory()) { // 否则如果它是一个目录
				// 声明目录下所有的文件 files[];
				File[] files = file.listFiles();
				// 遍历目录下所有的文件
				for (int i = 0; i < files.length; i++) {
					// 把每个文件 用这个方法进行迭代
					deleteFile(files[i]);
				}
			}
			file.delete();
		}
	}

	/**
	 * 删除文件
	 * @param filepath
	 * @throws IOException
	 */
	public static void deleteFile(String filepath) throws IOException {
		// 定义文件路径
		File f = new File(filepath);
		// 判断是文件还是目录
		if (f.exists() && f.isDirectory()) {
			// 若目录下没有文件则直接删除
			if (f.listFiles().length == 0) {
				f.delete();
			} else {
				// 若有则把文件放进数组，并判断是否有下级目录
				File[] files = f.listFiles();
				int i = f.listFiles().length;
				for (int j = 0; j < i; j++) {
					if (files[j].isDirectory()) {
						// 递归调用del方法并取得子目录路径
						deleteFile(files[j].getAbsolutePath());
					}
					// 删除文件
					files[j].delete();
				}
			}
		}
	}

	/**
	 * 读取文件中内容
	 * @param path
	 * @return
	 * @throws IOException
	 */
	public static String readFileToString(String path) throws IOException {
		String resultStr = null;
		FileInputStream fis = null;
		try {
			fis = new FileInputStream(path);
			byte[] inBuf = new byte[2000];
			int len = inBuf.length;
			int off = 0;
			int ret = 0;
			while ((ret = fis.read(inBuf, off, len)) > 0) {
				off += ret;
				len -= ret;
			}
			resultStr = new String(new String(inBuf, 0, off, "gbk").getBytes());
		} finally {
			if (fis != null)
				fis.close();
		}
		return resultStr;
	}

	/**
	 * 文件转成字节数组
	 * @param path
	 * @return
	 * @throws IOException
	 */
	public static byte[] readFileToBytes(String path) throws IOException {
		byte[] b = null;
		InputStream is = null;
		File f = new File(path);
		try {
			is = new FileInputStream(f);
			b = new byte[(int) f.length()];
			is.read(b);
		} finally {
			if (is != null)
				is.close();
		}
		return b;
	}

	/**
	 * 将byte写入文件中
	 * @param fileByte
	 * @param filePath
	 * @throws IOException
	 */
	public static void readByteToFile(byte[] fileByte, String filePath) throws IOException {
		OutputStream os = null;
		try {
			os = new FileOutputStream(new File(filePath));
			os.write(fileByte);
			os.flush();
		} finally {
			if (os != null)
				os.close();
		}
	}

	/**
	 * 读取文件
	 * @param filepath
	 * @param pathMap
	 * @return
	 * @throws Exception
	 */
	public static Map<Integer, String> readFile(String filepath, Map<Integer, String> pathMap) throws Exception {
		if (pathMap == null) {
			pathMap = new HashMap<Integer, String>();
		}
		// 文件
		File file = new File(filepath);
		if (!file.isDirectory()) {
			pathMap.put(pathMap.size(), file.getPath());
		} else if (file.isDirectory()) { // 如果是目录， 遍历所有子目录取出所有文件名
			String[] filelist = file.list();
			for (int i = 0; i < filelist.length; i++) {
				File readfile = new File(filepath + "/" + filelist[i]);
				if (!readfile.isDirectory()) {
					pathMap.put(pathMap.size(), readfile.getPath());
				} else if (readfile.isDirectory()) {
					// 子目录的目录
					readFile(filepath + "/" + filelist[i], pathMap);
				}
			}
		}
		return pathMap;
	}

	/**
	 * 获取图片文件实际类型,若不是图片则返回null
	 * @param file
	 * @return fileType
	 */
	public final static String getImageFileType(File file) {
		if (isImage(file)) {
			try {
				ImageInputStream iis = ImageIO.createImageInputStream(file);
				Iterator<ImageReader> iter = ImageIO.getImageReaders(iis);
				if (!iter.hasNext()) {
					return null;
				}
				ImageReader reader = iter.next();
				iis.close();
				return reader.getFormatName();
			} catch (Exception e) {
				logger.error(e.getMessage(), e);
				return null;
			}
		}
		return null;
	}

	/**
	 * 获取文件类型,包括图片,若格式不是已配置的,则返回null
	 * @param file
	 * @return fileType
	 */
	public final static String getFileByFile(File file) {
		String filetype = null;
		byte[] b = new byte[50];
		try {
			InputStream is = new FileInputStream(file);
			is.read(b);
			filetype = getFileTypeByStream(b);
			is.close();
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
		return filetype;
	}

	/**
	 * 通过数据流（二进制数据）判断文件类型
	 * @param b
	 * @return fileType
	 */
	public final static String getFileTypeByStream(byte[] b) {
		String magicNumberCode = String.valueOf(getFileHexString(b));
		if (StringUtil.isBlank(magicNumberCode)) {
			return FileType.getByMagicNumberCode(magicNumberCode.toUpperCase()).getFileTypeName();
		}
		return FileType.NOT_EXITS_ENUM.getFileTypeName();
	}

	/**
	 * isImage,判断文件是否为图片
	 * @param file
	 * @return true 是 | false 否
	 */
	public static final boolean isImage(File file) {
		boolean flag = false;
		try {
			BufferedImage bufreader = ImageIO.read(file);
			int width = bufreader.getWidth();
			int height = bufreader.getHeight();
            flag = width != 0 && height != 0;
		} catch (Exception e) {
			flag = false;
			logger.error(e.getMessage(), e);
		}
		return flag;
	}

	/**
	 * 通过文件路径判断文件类型
	 * @param path
	 * @return
	 * @throws IOException
	 */
	public static FileType getFileTypeByPath(String path) {
		// 获取文件头
		String magicNumberCode = null;
		try {
			magicNumberCode = getFileHeader(path);
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			return FileType.NOT_EXITS_ENUM;
		}
		if (StringUtil.isBlank(magicNumberCode)) {
			return FileType.getByMagicNumberCode(magicNumberCode.toUpperCase());
		}
		return FileType.NOT_EXITS_ENUM;
	}

	/**
	 * 通过文件路径获取文件头（即文件魔数）
	 * @param path
	 * @return
	 * @throws IOException
	 */
	public static String getFileHeader(String path) throws Exception {
		byte[] b = new byte[28];
		InputStream inputStream = null;
		try {
			inputStream = new FileInputStream(path);
			inputStream.read(b, 0, 28);
		} finally {
			if (inputStream != null) {
				inputStream.close();
			}
		}
		return getFileHexString(b);
	}

	/**
	 * 把文件二进制流转换成十六进制数据
	 * @param b
	 * @return fileTypeHex
	 */
	public final static String getFileHexString(byte[] b) {
		StringBuilder builder = new StringBuilder();
		if (b == null || b.length <= 0) {
			return null;
		}
		for (int i = 0; i < b.length; i++) {
			int v = b[i] & 0xFF;
			String hv = Integer.toHexString(v);
			if (hv.length() < 2) {
				builder.append(0);
			}
			builder.append(hv);
		}
		return builder.toString();
	}
}